#include <sol.hpp>
#include <glm/glm.hpp>
#include "CLog.h"

class CLua {
public:
    /**@strIsCode if is false: @param str represent a file path*/
    static bool load(sol::state& lua, sol::table& outTable, const std::string_view& str, bool strIsCode = true) {// c++17 support `std::string_view`
        try {
            /**@note if just load as config file, no function calls, no need to load lib!*/
            lua.open_libraries(sol::lib::base);
            sol::load_result result;
            if (strIsCode)
                result = lua.load(str);// load lua-script code
            else
                result = lua.load_file(std::string(str));// load lua-script file
            if (!result.valid())
                throw std::runtime_error("sol::load_result invalid! Failed to load Lua file!");
            outTable = result();// execute lua-script, getting the table
            if (!outTable.valid())
                throw std::runtime_error("sol::table invalid! Invalid Lua config table!");
        } catch (const sol::error& e) {
            LogE << "sol::error: " << e.what();
            return false;
        } catch (const std::exception& e) {
            LogE << "std::exception: " << e.what();
            return false;
        }
        return true;
    }

    static void readAsVec3(glm::vec3& outVec3, const sol::table& t) {
        if (t.size() != 3) throw std::runtime_error("Unexpected table array size!");
        outVec3.x = t[1];
        outVec3.y = t[2];
        outVec3.z = t[3];
    }

    template <typename T>
    static void parseArray(T& outValue1, T& outValue2, const sol::table& t) {// you can define more specific args(like t1,t2,t3,...)
        if (t.size() != 2) throw std::runtime_error("Unexpected table array size!");
        outValue1 = t[1];
        outValue2 = t[2];
    }

    /**@brief debug printing all fields*/
    static void print_table(const sol::table& tbl, const std::string& prefix = "") {
        if (!tbl.valid()) {
            LogE << prefix << "[invalid table]";
            return;
        }
        for (auto& [key, value] : tbl) {
            std::string key_str;
            if (key.is<std::string>())
                key_str = key.as<std::string>();
            else if (key.is<int>())
                key_str = std::to_string(key.as<int>());
            else
                key_str = "[unknown_key_type]";
            LogI << prefix << " = " << key_str;
            if (value.is<bool>())
                LogI << std::boolalpha << value.as<bool>();
            else if (value.is<int>())
                LogI << value.as<int>();
            else if (value.is<double>() || value.is<float>())
                LogI << value.as<float>();
            else if (value.is<std::string>())
                LogI << value.as<std::string>();
            else if (value.is<sol::table>())
                LogI << "{" << prefix << "}";
            else
                LogW << "[unprintable_type]";
        }
    }
};